<?php
/**
 * MineAdmin is committed to providing solutions for quickly building web applications
 * Please view the LICENSE file that was distributed with this source code,
 * For the full copyright and license information.
 * Thank you very much for using MineAdmin.
 *
 * @Author kiki
 * @Link   https://gitee.com/xmo/MineAdmin
 */

declare(strict_types=1);

namespace Mine\Aspect;

use App\Common\Service\CommonUserService;
use App\Common\Service\Tool\ConstantsService;
use App\Common\Service\Tool\JwtService;
use App\System\Model\Debug;
use Hyperf\Context\Context;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Di\Exception\Exception;
use Hyperf\HttpServer\Router\Dispatched;
use Mine\Annotation\ApiAuth;
use Mine\Annotation\Auth;
use Mine\Exception\NormalStatusException;
use Mine\Exception\TokenException;
use Mine\Helper\MineCode;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;

/**
 * Class AuthAspect
 * @package Mine\Aspect
 */
#[Aspect]
class ApiAuthAspect extends AbstractAspect
{

    #[Inject]
    protected RequestInterface $request;

    #[Inject]
    protected CommonUserService $userService;

    public $annotations = [
        ApiAuth::class
    ];

    /**
     * @param ProceedingJoinPoint $proceedingJoinPoint
     * @return mixed
     * @throws Exception
     */
    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        /** @var ApiAuth $auth */
        if (isset($proceedingJoinPoint->getAnnotationMetadata()->method[ApiAuth::class])) {
            $auth = $proceedingJoinPoint->getAnnotationMetadata()->method[ApiAuth::class];
        }

        $token = $this->request->getHeaderLine(ConstantsService::TOKEN);
        if (empty($token)) {
            $token = $this->request->input(ConstantsService::TOKEN);
            if (empty($token)) {
                throw new NormalStatusException('请先登录', MineCode::TOKEN_EXPIRED);
            }
        }
        $oriToken = $token;
        $token = explode('####', $token);
        if (count($token) != 2) {
            throw new NormalStatusException('token格式错误', MineCode::VALIDATE_FAILED);
        }
        if (!empty($token[1])) { // 用户id
            $key = ConstantsService::API_TOKEN . $token[1];
            $redisData = redis()->get($key);
            if (empty($redisData) || $redisData != $oriToken) {
                throw new NormalStatusException('token验证失败', MineCode::VALIDATE_FAILED);
            }

            // token续期
            $redisTime = redis()->ttl($key);
            if ($redisTime <= config('jwt.renew_ttl')) {
                redis()->set($key, $redisData, config('jwt.ttl'));
            }

            //设置全局用户id与设置全局用户信息
            Context::set(ConstantsService::API_USER_ID_NAME, $token[1]);
            $userDta = $this->userService->read((int)$token[1]);
            if (!empty($userDta) && is_object($userDta)) {
                $userDta->toArray();
            }
            Context::set(ConstantsService::API_USER_DATA, $userDta);

        } else {
            throw new NormalStatusException('token信息出错', MineCode::VALIDATE_FAILED);
        }
        // $this->checkJwtToken($token);

        return $proceedingJoinPoint->process();
    }

    public function checkJwtToken($token)
    {
        try {
            /** @var JwtService $jwt */
            $jwt = make(JwtService::class);
            $userId = $jwt->getUserIdByToken($token);
            if ($userId > 0) {
                //保存用户ID到协程上下文
                Context::set(ConstantsService::API_USER_ID_NAME, $userId);
            }
        } catch (\Throwable $e) {
            throw new NormalStatusException('jwt验证出错2', MineCode::TOKEN_EXPIRED);
        }
    }

    protected function getControllerAndAction()
    {
        $action = $this->request->getAttribute(Dispatched::class)->handler->callback;
        return ['controller' => $action[0], 'action' => $action[1]];
    }
}